/*
The surface-physics project: http://code.google.com/p/surface-physics/
Adds real world physics based on the Bullet physics engine, to bases in the Orbiter space
flight simulation software.

Written by Abhijit Nandy
License : Apache License 2.0

Bump_Heap.cpp
BulletBase management implementation.

This file implements the Bump_Heap class.

*/


/* Class declaration */
#include "Bump_Heap.h"


extern char boutput[1000];

struct packet pkt;
DWORD baseCmd;


/**
 * Constructor for the class
 * Will be used later, when objects created dynamically.
 * Do NOT allocate memory here, let init() do that
 */
Bump_Heap::Bump_Heap()
{
	mode = IDLE;
	numObjects = 0;	
	physicsWorldOffset.setValue(0.f, 0.f, 0.f);

	cutoff = 2.0;
}


/**
 * Destructor, does nothing currently
 */
Bump_Heap::~Bump_Heap()
{}


/**
 * Initialize a base from its OBJHANDLE.
 * Creates a new BulletBase and inserts it in the Orbiter world
 *
 */
void
Bump_Heap::init(OBJHANDLE hObj, OBJHANDLE hObjRBody)
{
	int i;
	VESSELSTATUS2 vsBB;
	VECTOR3 gposMoon;


	oapiWriteLog("Bump_Heap::init: Initializing...");

	//This will change midway through the sim ?
	hObjRefBody = hObjRBody;
	hObjBase = hObj;
	oapiGetObjectName(hObjBase, strBaseName, 20);


	//Create a BulletBase VESSEL at the origin of Brighton Beach,
	//get equatorial location(latitude, long location on 2d moon surface)


	//oapiGetBasePadEquPos(hObjBase, 0, &lng, &lat, &rad);//To create base at pad 1 for DEBUG

	oapiGetBaseEquPos(hObjBase, &lng, &lat, &rad);

	//Convert to global ecliptic co-ord.
	oapiEquToGlobal(hObjBase, lng, lat, rad, &gposBase);

	//Get reference body(nearest celestial body) co-ordinates for BB rpos calc.
	oapiGetGlobalPos(hObjRefBody, &gposMoon);

	//Zero out the VESSELSTATUS2 structure to prevent unwanted vessel states
	memset(&vsBB,0,sizeof(VESSELSTATUS2));
	vsBB.version = 2;
	vsBB.status = 1; //landed
	vsBB.rbody = hObjRefBody;
	vsBB.rpos = gposBase - gposMoon;
	vsBB.surf_lng = lng;
	vsBB.surf_lat = lat;
	vsBB.surf_hdg = PI/2;


	//Create the BulletBase in a landed state with same position as the passed Base
	hObjBulletBase = oapiCreateVesselEx("Bump Brighton Beach", "BulletBase", &vsBB);

	//Cast and keep proper pointers to the associated BulletBase
	vBulletBase = (VESSEL3*)(oapiGetVesselInterface(hObjBulletBase));
	bb = (BulletBase*)(vBulletBase);
	vBulletBase->SetSize(BULLET_WORLD_SIZE);

	//Null all object pointers contained in this base
	for (i = 0; i < MAX_OBJECTS; i++)
		bbObj[i] = NULL;


	mode = CREATED_BB;

	//TODO: Read physics file for properties like cutoff ?
	//readPhysicsFile();

	sprintf(boutput, "Bump_Heap::init : Created BulletBase %s", strBaseName);
	oapiWriteLog(boutput);



}


/**
 * Updates the following:
 * 1. Opens the scenario specific basename.txt physics file from the Physics folder
 * 2. Attaches or creates the listed objects in the file over a number of sim steps
 * 3. Continues to update attachment points with transforms or send them to BB if needed
 * 4. Processes commands from all objects managed by this BB and BB itself by reading member
 *
 * TODO: Implement frequency of updates list and do not update inactive objects or static geometry objects
 */
void
Bump_Heap::prestep(double SimT, double SimDT, double MJD)
{

}


/**
 * Updates the following:
 * 1. Opens the scenario specific basename.txt physics file from the Physics folder
 * 2. Attaches or creates the listed objects in the file over a number of sim steps
 * 3. Continues to update attachment points with transforms or send them to BB if needed
 * 4. Processes commands from all objects managed by this BB and BB itself by reading member
 *
 * TODO: Implement frequency of updates list and do not update inactive objects or static geometry objects
 */
void
Bump_Heap::poststep(double SimT, double SimDT, double MJD)
{
	int i;
	char *line;

	//sprintf(boutput, "Bump_Heap::update: mode = %d", mode);
	//oapiWriteLog(boutput);

	if(mode == CREATED_BB){
		//Open base name file and start parsing it

		physicsFile = oapiOpenFile("Bump/BrightonBeach.txt", FILE_IN , ROOT);

		if(physicsFile){
			sprintf(boutput, "Bump_Heap::update: File opened");
			oapiWriteLog(boutput);
			mode = OPENED_FILE;
		}
		else{
			sprintf(boutput, "Bump_Heap::update: ERROR : File could not be opened");
			oapiWriteLog(boutput);
			mode = IDLE;
		}
	}
	else if(mode == OPENED_FILE){
		//Start parsing file and injecting objects, store their pointers


		//Read a object from the physics file of this base, till EOF, do not check for MAX_OBJECTS here
		//Its checked later already with a warning presented if numObjects == MAX_OBJECTS
		if( physicsFile &&			
			oapiReadScenario_nextline (physicsFile, line)) {
			
			sprintf(boutput, "Bump_Heap::update: %s", line);
			oapiWriteLog(boutput);

			if(strcmp(line, "BEGIN_DESC") == 0){
				readPhysicsFileDescription();
			}

			//Description read, now parse and create 1 object per step
			readCreatePhysicsFileObject();


			//sscanf (line+9, "%s", &my_value);
		}
		else{
			//All lines read, close file
			oapiCloseFile(physicsFile, FILE_IN);
			mode = PARSED_FILE;
			sprintf(boutput, "Bump_Heap::update: Finished reading file : %p", physicsFile);
			oapiWriteLog(boutput);
		}

	}
	else if(mode == PARSED_FILE){
		//Normal updation of transforms and command processing

		//vBulletBase->clbkGeneric(1, 1, (void*)&pkt);
		//Parse any base commands first
		baseCmd = bb->baseCmd;


		//Now update the containing objects
		for (i = 0; i < numObjects; i++)
			bbObj[i]->poststep(SimT, SimDT, MJD);

		refreshSections();


	}//end if









	/*sprintf(output, "Bump: The next sim step will advance the sim by %f secs", SimDT);
	oapiWriteLog(output);*/



}


/**
 * Free all objects first, de-allocating their memory
 * Each object will clears it handles etc and any handles it made with it's current BulletBase
 * (if a VESSEL was attached/detached midway in the sim, it should have properly cleared the base handle)
 *
 * Sets all values in base object back to default in case this object is reused
 */
void
Bump_Heap::exit(void)
{
	int i, rv;

	//Cleanup all objects, if exit() fails on a sub object, return error, but still delete it's ptr
	for (i = 0; i < numObjects; i++){
		rv = bbObj[i]->exit();
		if(rv){
			sprintf(boutput, "Bump_Heap::exit: ERROR : exit() failed on sub class object %s " \
								"create physics counterpart %s", bbObj[i]->strObjectName);
			oapiWriteLog(boutput);
		}

		delete bbObj[i];
	}


	//Base cleanup
	mode = IDLE;
	numObjects = 0;
	physicsWorldOffset.setValue(0.f, 0.f, 0.f);
}

/**
 * Utility function to read the description from a physics file
 */
void
Bump_Heap::readPhysicsFileDescription(void)
{
	char *line;

	while(physicsFile && oapiReadScenario_nextline (physicsFile, line)) {
		sprintf(boutput, "Bump_Heap::readPhysicsFileDescription: %s", line);
		oapiWriteLog(boutput);

		if(strcmp(line, "END_DESC") == 0){
			break;
		}
	}

}


/**
 * Utility function to read 1 object from a physics file and create it
 * Will return immediately if END or END_OBJECTS found
 * Skips blank lines or lines not containing ':'
 *
 * TODO: Move object creation to separate function
 */
void
Bump_Heap::readCreatePhysicsFileObject(void)
{
	char *line, *sep;
	char strObjName[MAX_STRLEN], strType[MAX_STRLEN];
	
	//Read the next line and create the object
	while(physicsFile && oapiReadScenario_nextline (physicsFile, line)) {
		sprintf(boutput, "Bump_Heap::readCreatePhysicsFileObject: %s", line);
		oapiWriteLog(boutput);

		//Skip blank lines
		if(strlen(line) == 0 || line[0] == ';')
			continue;

		//Don't parse section labels
		if((strcmp(line, "END") == 0) ||
		   (strcmp(line, "END_OBJECTS") == 0)
		   ){
			break;
		}

		//Should now be an object descriptor line, check for ':', else skip
		if((sep = strchr(line, ':'))){

			//Get the name and type as strings
			strncpy(strObjName, line, sep - line);
			strObjName[sep - line] = '\0';
			strcpy(strType, sep + 1);


			sprintf(boutput, "Bump_Heap::readCreatePhysicsFileObject: Object Name:%s, Type:%s",
					strObjName, strType);
			oapiWriteLog(boutput);

			createBaseObject(strType, strObjName, line);

		}


	}

}


btRigidBody*
Bump_Heap::localCreateRigidBody(float mass, const btTransform& startTransform,btCollisionShape* shape)
{
	btAssert((!shape || shape->getShapeType() != INVALID_SHAPE_PROXYTYPE));

	//rigidbody is dynamic if and only if mass is non zero, otherwise static
	bool isDynamic = (mass != 0.f);

	btVector3 localInertia(0,0,0);
	if (isDynamic)
		shape->calculateLocalInertia(mass,localInertia);

	//using motionstate is recommended, it provides interpolation capabilities, and only synchronizes 'active' objects

	btRigidBody* body = new btRigidBody(mass,0,shape,localInertia);
	body->setWorldTransform(startTransform);
	body->setContactProcessingThreshold(BT_LARGE_FLOAT);
	m_dynamicsWorld->addRigidBody(body);

	return body;
}


/**
 * create object using type name and switch-case
 */
void
Bump_Heap::createBaseObject(char *strType, char *strObjName, char *line)
{
	int rv;

	//Now create the proper subtype of Bump_BaseObject
	if(strcmp(strType,"BulletBaseVehicle")==0 && numObjects>=0 && numObjects<MAX_OBJECTS){

		//Set the base right away
		bbObj[numObjects] = new Bump_ATV(this, strObjName);

		//<>createTypedBaseObject();

		//Find or create strObjName and attach to base, if rv!=0, deallocate imm.
		//Separate function init() used to find/create object as it can return an error value
		//Constructor cant.
		rv = bbObj[numObjects]->init();

		//Increment only if object created, else de-allocation from bbObj[] will cause crash
		if(!rv){
			numObjects++;
		}
		else{
			//De-allocate!, something went wrong, do not allocate memory in constructor
			delete bbObj[numObjects];
			bbObj[numObjects] = NULL;

			sprintf(boutput, "Bump_Heap::createBaseObject: ERROR : "\
					"init() failed on subclass %s object: %s with rv=%d, Deleted object",
					strType, strObjName, rv);
			oapiWriteLog(boutput);
		}
	}
	else if(strcmp(strType,"ExternalStaticGeometry")==0 && numObjects>=0 && numObjects<MAX_OBJECTS){

		//Set the base right away
		bbObj[numObjects] = new Bump_ExternalStaticGeometry(this, strObjName);

		rv = bbObj[numObjects]->init();

		//Increment only if object created, else de-allocation from bbObj[] will cause crash
		if(!rv){
			numObjects++;
		}
		else{
			//De-allocate!, something went wrong, do not allocate memory in constructor
			delete bbObj[numObjects];
			bbObj[numObjects] = NULL;

			sprintf(boutput, "Bump_Heap::createBaseObject: ERROR : "\
					"init() failed on subclass %s object: %s with rv=%d, Deleted object",
					strType, strObjName, rv);
			oapiWriteLog(boutput);
		}
	}
	else if(strcmp(strType,"Ball")==0 && numObjects>=0 && numObjects<MAX_OBJECTS){

		//Set the base right away
		bbObj[numObjects] = new Bump_Ball(this, strObjName);

		rv = bbObj[numObjects]->init();

		//Increment only if object created, else de-allocation from bbObj[] will cause crash
		if(!rv){
			numObjects++;
		}
		else{
			//De-allocate!, something went wrong, do not allocate memory in constructor
			delete bbObj[numObjects];
			bbObj[numObjects] = NULL;

			sprintf(boutput, "Bump_Heap::createBaseObject: ERROR : "\
					"init() failed on subclass %s object: %s with rv=%d, Deleted object",
					strType, strObjName, rv);
			oapiWriteLog(boutput);
		}
	}
	else if(strcmp(strType,"Plate")==0 && numObjects>=0 && numObjects<MAX_OBJECTS){

		//Set the base right away
		bbObj[numObjects] = new Bump_Plate(this, strObjName);

		rv = bbObj[numObjects]->init();

		//Increment only if object created, else de-allocation from bbObj[] will cause crash
		if(!rv){
			numObjects++;
		}
		else{
			//De-allocate!, something went wrong, do not allocate memory in constructor
			delete bbObj[numObjects];
			bbObj[numObjects] = NULL;

			sprintf(boutput, "Bump_Heap::createBaseObject: ERROR : "\
					"init() failed on subclass %s object: %s with rv=%d, Deleted object",
					strType, strObjName, rv);
			oapiWriteLog(boutput);
		}
	}
	else{
		sprintf(boutput, "Bump_Heap::createBaseObject: %s", line);

		if(numObjects == MAX_OBJECTS)
			sprintf(boutput, "%s, ERROR Number of objects:%d = MAX_OBJECTS %d",
				boutput, numObjects, MAX_OBJECTS);

		oapiWriteLog(boutput);

	}
}

/**
 * WIP, create sub class object based on a table and template
 */
template<class T>
void
Bump_Heap::createTypedBaseObject() {
    if( !bbObj[numObjects] )
    	bbObj[numObjects] = new T;
}


/**
 * Called to refresh list of active sections, priority is TO DELETE sections as much as possible
 * to keep number of active sections absolutely minimal.
 *
 * All deletions/loads based on camera position and adjacency graph.
 *
 * Graph can mark certain sections as never to be removed(such as external sections)
 *
 * 1. Loads sections from file based on camera position : should be threaded off
 * 2. Unloads them if their deleteCountdown has reached 0
 * 3. Updates the deleteCountdown of active sections
 *
 *
 *
 */
void
Bump_Heap::refreshSections(void)
{

	//Convert camera position in base relative co-ordinates

	VECTOR3 rposCamera, gposCamera;

	//TODO: Move this out of here, multiple bases should make just 1 call to camera position
	oapiCameraGlobalPos(&gposCamera);


	//The bases can convert the camera position to their own relative co-ords as needed
	bb->Global2Local(gposCamera, rposCamera);



	//sprintf(oapiDebugString(), "Camera at : %f, %, %f", rposCamera.x,  rposCamera.y,  rposCamera.z);

	//Every sections has its cutoff(section unloaded after camera further than this point or most stuff unloaded
	//:idling)




}

